#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>

#include "proto.h"
#include "media_lib.h"
#include "thr_channel.h"

#define  CHANNEL_NUM  200
//#define DATA_MAX	25000
//在100～110之间，110时一开始像是跟不上
//103时差不多，先跟不上，再良好，后期丢包
#define DATA_MAX	210
#define TIME_OUT_SEC 0
#define TIME_OUT_USEC 10000

#define  NONE	-1
#define	 WAIT	0
#define  RUN	1
#define  OVER	2

static pthread_mutex_t mut= PTHREAD_MUTEX_INITIALIZER;
static struct sigaction alrm_sa_save;
static struct itimerval old_itv;
static int inited = 0;


//本文所使用的结构体
struct mlib_chn_data
{
	pthread_mutex_t mut2;
	pthread_cond_t cond;
	int token;	//令牌数
	int ptr;	//偏移量	
	int flag;    //标记该频道的状态
	struct mlib_chn_data_st *mcds;  //从medialib获取的数据
};
static	struct mlib_chn_data mcd[CHANNEL_NUM+1];
static	struct msg_channel_st *mcs[CHANNEL_NUM+1];


//信号处理函数
static void alrm_sa(int s,siginfo_t *infop,void *unused)
{
	int i;
	for(i=1;i<=CHANNEL_NUM;i++)
	{
		if(mcd[i].flag==WAIT||mcd[i].flag==RUN)
		{
			pthread_mutex_lock(&mcd[i].mut2);
			mcd[i].token = mcd[i].token+1;
			pthread_cond_broadcast(&mcd[i].cond);
			pthread_mutex_unlock(&mcd[i].mut2);
		}
	}
}
//当调用exit时，会被调用

static void module_unload()
{
	sigaction(SIGALRM,&alrm_sa_save,NULL);
	setitimer(ITIMER_REAL,&old_itv,NULL);
}

//将信号集合和时间进行初始化，并挂到钩子函数上。
static void module_load()
{
	struct sigaction sa;
	struct itimerval itv;
	int i;
	sa.sa_sigaction = alrm_sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_SIGINFO;
	if(sigaction(SIGALRM,&sa,&alrm_sa_save)<0)
	{
		perror("sigaction()");
		exit(1);
	}
	itv.it_interval.tv_sec = TIME_OUT_SEC;
	itv.it_interval.tv_usec = TIME_OUT_USEC;
	itv.it_value.tv_sec = TIME_OUT_SEC;
	itv.it_value.tv_usec = TIME_OUT_USEC;
	if(setitimer(ITIMER_REAL,&itv,&old_itv) < 0)
	{
		perror("setitimer()");
		exit(1);
	}
	for(i=1;i<=CHANNEL_NUM;i++)
	{
		mcd[i].flag = NONE;
	}
	atexit(module_unload);
}


//取令牌，每次只取一个，没有则等待
static int mytbf_fetchtoken(struct mlib_chn_data *ptr,int size)
{
	struct mlib_chn_data *me = ptr;
	if(size <= 0)
		return -EINVAL;
	pthread_mutex_lock(&me->mut2);
	while(me->token<=0) {
		pthread_cond_wait(&me->cond,&me->mut2);
	}
	me->token = me->token - size;
	pthread_mutex_unlock(&me->mut2);
	return size;
}


void * thr_channel_create(void *p)
{
	int *num=p;
	int size,getsize;
	int i=0;
	//开始计时，并在每一秒后加一个令牌

	pthread_mutex_lock(&mut);
	if(!inited)
	{
		module_load();
		inited = 1;
	}
	pthread_mutex_unlock(&mut);
	//初始化本文件使用结构体的令牌等信息
	for(i=num[0];i<=num[1];i++)
	{
	//	printf("为%d频道开辟空间\n",i);
		pthread_mutex_lock(&mut);
		pthread_mutex_init(&mcd[i].mut2,NULL);
		pthread_cond_init(&mcd[i].cond,NULL);
		mcd[i].flag = WAIT;
		mcd[i].token = 1;
		mcs[i] = malloc(sizeof(struct msg_channel_st)+DATA_MAX-1);
		if(mcs[i]==NULL)
		{
			perror("malloc()");
			exit(1);
		}
		pthread_mutex_unlock(&mut);
	}
	while(1)
	{
		for(i=num[0];i<=num[1];i++)
		{
			//从medialib中获得存储数据内存的内存地址
			if(mcd[i].flag==WAIT)
			{
				mcd[i].ptr = 0;
				mcd[i].mcds = mlib_getchndata((chnid_t)i);
				//如果内存地址位空，则表示该频道播放完毕。
				if(mcd[i].mcds ==  NULL)
				{
					mcd[i].flag = OVER;
				}
				else {
					mcd[i].flag = RUN;
				}
			}
			while(mcd[i].flag==RUN)
			{
				//取令牌
				getsize = DATA_MAX;
				size = mytbf_fetchtoken(&mcd[i],1);
				if(size<0)
				{
					fprintf(stderr,"%s\n",strerror(-size));
					exit(1);
				}
				//从内存中取数据
				if(mcd[i].ptr+DATA_MAX > mcd[i].mcds->datasize)
					getsize = mcd[i].mcds->datasize - mcd[i].ptr;
				memcpy(mcs[i]->data,mcd[i].mcds->data+mcd[i].ptr,getsize);
				mcs[i]->chnid =htons(mcd[i].mcds->chnid);
				
				if(sendto(sd,mcs[i],sizeof(struct msg_channel_st)+getsize-1,0,(void *)&raddr,sizeof(raddr))<0)
				{
					perror("sendto()");
					exit(1);
				}

				//判断该文件数据是否读完，如果读完就会再取数据获取内存地址
				mcd[i].ptr = mcd[i].ptr + getsize;
				if(mcd[i].ptr >= mcd[i].mcds->datasize)
				{
					mlib_freechndata(mcd[i].mcds);
					mcd[i].ptr = 0;
					mcd[i].flag = WAIT;
				}
				//判断是否有令牌积累，如果有继续取令牌
				if(mcd[i].token==0)
					break;

			}
		}

	}


}
//销毁数据报
int thr_channel_destroy()
{
	int i=1;
	for(i=1;i<=CHANNEL_NUM;i++)
	{
		pthread_mutex_destroy(&mcd[i].mut2);
		free(mcs[i]);
	}
	pthread_mutex_destroy(&mut);
	return 0;
}
